package com.daffodil.util;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import javax.persistence.Column;

import com.daffodil.core.annotation.Hql;
import com.daffodil.core.annotation.Hql.Logical;
import com.daffodil.core.entity.Query;
import com.daffodil.util.ReflectUtils;
import com.daffodil.util.StringUtils;
import com.daffodil.util.text.Convert;

import lombok.extern.slf4j.Slf4j;

/**
 * hql操作辅助工具类
 * 
 * @author yweijian
 * @date 2019年8月18日
 * @version 1.0
 */
@Slf4j
public class HqlUtils {
    
    /**
     * 仅支持字母、数字、下划线、空格、逗号（支持多个字段排序）
     */
    public static final String HQL_PATTERN = "[a-zA-Z0-9_\\ \\,]+";

    /**
     * 检查字符，防止注入绕过,或字段不存在字段绕过
     */
    public static String escapeOrderByHql(String value, Object entity) {
        List<String> columns = getEntityColumnName(entity);
        if(columns.size() <= 0) {
            return "";
        }
        if(StringUtils.isEmpty(value)) {
            return "";
        }
        if(!isValidOrderByHql(value)) {
            return "";
        }
        List<String> orders = getOrderColumn(value);
        if(!columns.containsAll(orders)) {
            return "";
        };
        return value;
    }
    
    /**
     * 获取有效的排序字段
     * @param value
     * @return
     */
    public static List<String> getOrderColumn(String value){
        List<String> orders = new ArrayList<String>();
        if(StringUtils.isNotEmpty(value)) {
            String column = "";
            char[] charts = value.toCharArray();
            for(int i = 0; i < charts.length; i++) {
                char chart = value.charAt(i);
                if(' ' == chart || ',' == chart) {
                    if(StringUtils.isNotEmpty(column) && !column.equalsIgnoreCase("desc") && !column.equalsIgnoreCase("asc")) {
                        orders.add(column);
                    }
                    column = "";
                }else {
                    column += String.valueOf(chart);
                }
            }
        }
        return orders;
    }

    /**
     * 验证 order by 语法是否符合规范
     */
    public static boolean isValidOrderByHql(String value) {
        return value.matches(HQL_PATTERN);
    }
    
    /**
     * 生成辅助的hql条件语句，时间范围检索字段默认createTime
     * @param hql 
     * @param paras 
     * @param query 
     */
    public static void createHql(StringBuffer hql, List<Object> paras, Query<?> query){
        createHql(hql, paras, query, "");
    }
    /**
     * 生成辅助的hql条件语句，时间范围检索字段默认createTime
     * @param hql 
     * @param paras
     * @param query
     * @param alias 别名
     */
    public static void createHql(StringBuffer hql, List<Object> paras, Query<?> query, String alias){
        createHql(hql, paras, query, alias, "createTime");
    }
    
    /**
     * 生成辅助的hql条件语句
     * @param hql
     * @param paras
     * @param query
     * @param alias 别名
     * @param timefield 时间范围检索字段
     */
    public static void createHql(StringBuffer hql, List<Object> paras, Query<?> query, String alias, String timefield) {
        createHql(hql, paras, query, alias, timefield, "");
    }
    
    /**
     * 
     * @param hql
     * @param paras
     * @param query
     * @param alias 别名
     * @param timefield 时间范围检索字段
     * @param ignoreFields 忽略字段
     */
    public static void createHql(StringBuffer hql, List<Object> paras, Query<?> query, String alias, String timefield, String... ignoreFields) {
        Object entity = query.getEntity();
        if(StringUtils.isNotNull(entity)){
            createHql(hql, paras, entity, alias, ignoreFields);
        }
        
        alias = StringUtils.isNotEmpty(alias) ? alias.trim() + "." : "";
        
        if(StringUtils.isNotNull(query.getStartTime()) && StringUtils.isNotEmpty(timefield)){
            hql.append(StringUtils.format(" and {}{} >= ? ", alias, timefield));
            paras.add(query.getStartTime());
        }
        if(StringUtils.isNotNull(query.getEndTime()) && StringUtils.isNotEmpty(timefield)){
            hql.append(StringUtils.format(" and {}{} <= ? ", alias, timefield));
            paras.add(query.getEndTime());
        }
        if(StringUtils.isNotEmpty(query.getDataScope())){
            hql.append(query.getDataScope());
        }
        String order = escapeOrderByHql(query.getOrderBy(), entity);
        if(StringUtils.isNotEmpty(order)){
            if(StringUtils.isNotEmpty(alias)) {
                List<String> orders = getOrderColumn(order);
                for(String item : orders) {
                    order = order.replace(item, alias + item);
                }
            }
            hql.append(StringUtils.format(" order by {}", order));
        }
    }
    
    /**
     * 生成辅助的hql条件语句
     * @param hql
     * @param paras
     * @param entity
     * @param alias
     */
    public static void createHql(StringBuffer hql, List<Object> paras, Object entity, String alias){
        createHql(hql, paras, entity, alias, "");
    }
    
    /**
     * 
     * @param hql
     * @param paras
     * @param entity
     * @param alias
     * @param ignoreFields 忽略字段
     */
    public static void createHql(StringBuffer hql, List<Object> paras, Object entity, String alias, String... ignoreFields){
        if(StringUtils.isNull(entity)){
            return;
        }
        alias = StringUtils.isNotEmpty(alias) ? alias.trim() + "." : "";
        
        List<String> ignores = new ArrayList<String>(ignoreFields.length);
        Collections.addAll(ignores, ignoreFields);
        
        Field[] fields = getEntityField(entity);
        for(Field field : fields){
            if(StringUtils.isNotEmpty(ignores) && ignores.contains(field.getName())) {
               continue;
            }
            Hql annotation = getHqlAnnotation(field, entity);
            if(StringUtils.isNotNull(annotation)){
                Logical logical = annotation.type();
                try {
                    Object value = ReflectUtils.invokeGetter(entity, field.getName());
                    if(value instanceof String){
                        if(StringUtils.isNotNull(value) && StringUtils.isNotEmpty(value.toString())){
                            if(Logical.LIKE.name().equals(logical.name())){
                                hql.append(StringUtils.format(" and {}{} {} ? ", alias, field.getName(), logical.value()));
                                paras.add("%" + value.toString().trim() + "%");
                            }else if(Logical.LLIKE.name().equals(logical.name())){
                                hql.append(StringUtils.format(" and {}{} {} ? ", alias, field.getName(), logical.value()));
                                paras.add(value.toString().trim() + "%");
                            }else if(Logical.RLIKE.name().equals(logical.name())){
                                hql.append(StringUtils.format(" and {}{} {} ? ", alias, field.getName(), logical.value()));
                                paras.add("%" + value.toString().trim());
                            }else if(Logical.IN.name().equals(logical.name())){
                                String in = HqlUtils.createHql(paras, Convert.toStrArray((String)value));
                                hql.append(StringUtils.format(" and {}{} {}{} ", alias, field.getName(), logical.value(), in));
                            }else{
                                hql.append(StringUtils.format(" and {}{} {} ? ", alias, field.getName(), logical.value()));
                                paras.add(value.toString().trim());
                            }
                        }
                    }else{
                        if(StringUtils.isNotNull(value)){
                            hql.append(StringUtils.format(" and {}{} {} ? ", alias, field.getName(), logical.value()));
                            paras.add(value);
                        }
                    }
                } catch (Exception e) {
                    log.warn(e.getMessage(), e);
                }
            }
        }
    }
    
    /**
     * -获取对象实体字段
     * @param entity
     * @return
     */
    public static Field[] getEntityField(Object entity){
        if(StringUtils.isNotNull(entity)) {
            return entity.getClass().getDeclaredFields();
        }
        return new Field[0];
    }
    
    /**
     * -获取对象实体字段名称
     * @param entity
     * @return
     */
    public static List<String> getEntityColumnName(Object entity){
        Field[] fields = getEntityField(entity);
        List<String> columns = new ArrayList<String>();
        for(int i = 0; i < fields.length; i++){
            Column annotation = fields[i].getAnnotation(Column.class);
            if(StringUtils.isNotNull(annotation)) {
                columns.add(fields[i].getName());
            }
        }
        return columns;
    }
    
    /**
     * 获取Hql注解
     * @param field
     * @param entity
     * @return
     */
    private static Hql getHqlAnnotation(Field field, Object entity) {
        Hql annotation = field.getAnnotation(Hql.class);
        if(StringUtils.isNull(annotation)){
            try {
                Method get = entity.getClass().getMethod("get" + StringUtils.capitalize(field.getName()));
                annotation = StringUtils.isNotNull(get) ? get.getAnnotation(Hql.class) : null;
            } catch (NoSuchMethodException | SecurityException e) {
            }
        }
        return annotation;
    }
    
    /**
     * 生成辅助的hql条件语句
     * @param hql 
     * @param paras
     * @param ids
     * @return ( id1, id2, id3 )
     */
    public static String createHql(List<Object> paras, Object[] ids){
        StringBuffer hql = new StringBuffer();
        if(StringUtils.isEmpty(ids)){
            return hql.toString();
        }
        hql.append("(");
        for(int i = 0; i < ids.length; i++){
            hql.append("?");
            if(i == (ids.length - 1)){
                hql.append(") ");
            }else{
                hql.append(", ");
            }
            paras.add(ids[i]);
        }
        return hql.toString();
    }

}
